<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>添加箭头动画</title>
    <link href="ol/ol.css" rel="stylesheet" type="text/css"/>
    <script src="ol/ol.js" type="text/javascript"></script>
    <style type="text/css">
        #mapCon {
            width: 100%;
            height: 100%;
        }
    </style>
</head>
<body>
<!-- 地图容器 -->
<div id="mapCon"></div>
<div style="position: absolute;top: 20px;left: 50%;">
    运动速度:<input id="speed" type="range" min="1" max="10" step="1" value="5"/>
    <button id="start-animation">开始</button>
</div>
<script type="text/javascript">
    var key = "4689fc6b9bc0fdc8c48298f751ebfb41";//天地图密钥

    //ol.layer.Tile：是一个瓦片图层类，用于显示瓦片资源。
    //source是必填项，用于为图层设置来源。

    //ol.source.XYZ:
    //创建天地图矢量图层
    var TiandiMap_vec = new ol.layer.Tile({
        title: "天地图矢量图层",
        source: new ol.source.XYZ({
            url: "http://t{0-7}.tianditu.com/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=" + key,
            wrapX: false
        })
    });
    //创建天地图矢量注记图层
    var TiandiMap_cva = new ol.layer.Tile({
        title: "天地图矢量注记图层",
        source: new ol.source.XYZ({
            url: "http://t{0-7}.tianditu.com/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=" + key,
        })
    });
    //实例化Map对象加载地图
    var map = new ol.Map({
        //地图容器div的ID
        target: 'mapCon',
        //地图容器中加载的图层
        layers: [TiandiMap_vec, TiandiMap_cva],
        //地图视图设置
        view: new ol.View({
            //地图初始中心点（经纬度）
            center: [118.37, 37.14],
            //地图初始显示级别
            zoom: 12,
            projection: "EPSG:4326"
        })
    });

    /**
     * 线性插值
     * @param start 线段开始点的坐标
     * @param end 结束点坐标
     * @param step 步长
     * @returns {*[]}
     */
    function interpolate(start, end, step) {
        const x = start[0] + step * (end[0] - start[0]);
        let num = Math.ceil(Math.abs(end[0] - start[0]) / step);
        let xArr = [];
        setp = end[0] > start[0] ? step : 0 - step;
        for (let i = 1; i < num; i++) {
            xArr.push(start[0] + setp * i);
        }
        //插入起点
        let lonlat = [start];
        for (let i = 0; i < xArr.length; i++) {
            let y = start[1] + (end[1] - start[1]) / (end[0] - start[0]) * (xArr[i] - start[0]);
            lonlat.push([xArr[i], y]);
        }
        //加入终点
        lonlat.push(end);
        return lonlat;
    }

    //构建一组离散化的点（一组线段的起止点）
    var Coordinates = [
        {
            lonlat: [[118.37, 37.14], [118.48, 37.05]]
        },
        {
            lonlat: [[118.46, 37.04], [118.51, 37.06]]
        }
    ];
    //遍历线段，并根据起止点，计算插值数据，用于动画
    let routes = [];//路径对象数组
    for (let i = 0; i < Coordinates.length; i++) {
        const item = Coordinates[i];
        //获取插值数据
        let lonlatArr = interpolate(item.lonlat[0], item.lonlat[1], 0.01);
        //将插值数据构建为Line对象
        let route = new ol.geom.LineString(lonlatArr);
        //获取直线的坐标
        let routeCoords = route.getCoordinates();
        let routeLength = routeCoords.length;

        let routeFeature = new ol.Feature({
            type: 'route',
            geometry: route
        });
        let geoMarker = new ol.Feature({
            type: 'geoMarker',
            geometry: new ol.geom.Point(routeCoords[0])
        });
        routes.push(
            {
                route: route,
                routeCoords: routeCoords,
                routeLength: routeLength,
                routeFeature: routeFeature,
                geoMarker, geoMarker,
                index: 0
            }
        )
    }


    var styles = {
        'route': new ol.style.Style({
            stroke: new ol.style.Stroke({
                width: 6,
                color: [237, 212, 0, 0.8]
            })
        }),
        'icon': new ol.style.Style({
            image: new ol.style.Icon({
                anchor: [0.5, 1],
                src: "../../images/stationicon.png"
            })
        }),
        'geoMarker': new ol.style.Style({
            image: new ol.style.Circle({
                radius: 7,
                snapToPixel: false,
                fill: new ol.style.Fill({color: 'black'}),
                stroke: new ol.style.Stroke({
                    color: 'white',
                    width: 2
                })
            })
        })
    };

    var animating = false;
    var speed, now;
    var speedInput = document.getElementById('speed');
    var startButton = document.getElementById('start-animation');
    //添加用于展示动画的层
    var vectorSource = new ol.source.Vector();
    var vectorLayer = new ol.layer.Vector({
        source: vectorSource,
        style: function (feature) {
            //如果动画是激活的就隐藏geoMarker
            if (animating && feature.get('type') === 'geoMarker') {
                return null;
            }
            return styles[feature.get('type')];
        }
    });

    map.addLayer(vectorLayer);
    for (let i = 0; i < routes.length; i++) {
        let item = routes[i];
        vectorSource.addFeature(item.routeFeature);
        vectorSource.addFeature(item.geoMarker);
    }
    var moveFeature = function (event) {
        var vectorContext = event.vectorContext;
        var frameState = event.frameState;

        if (animating) {

            //通过增加速度，来获得lineString坐标
            for (let i = 0; i < routes.length; i++) {
                const item = routes[i];
                var elapsedTime = frameState.time - item.now;
                item.index = Math.round(speed * elapsedTime / 1000);
                if (item.index >= item.routeLength) {
                    //重置时间和各个线段的下标，用于循环
                    item.now = new Date().getTime();
                    item.index = 0;
                    //执行下面两行代码 自动结束动画
                    //stopAnimation(true);
                    //return;
                }

                var currentPoint = new ol.geom.Point(item.routeCoords[item.index]);
                var feature = new ol.Feature(currentPoint);
                vectorContext.drawFeature(feature, styles.geoMarker);
            }

        }
        //继续动画效果
        map.render();
    };

    function startAnimation() {
        if (animating) {
            stopAnimation(false);
        } else {
            animating = true;
            speed = speedInput.value;
            startButton.textContent = '结束运动';
            //隐藏geoMarker
            for (let i = 0; i < routes.length; i++) {
                const item = routes[i];
                item.geoMarker.setStyle(null);
                item.now = new Date().getTime();
            }

            //设置显示范围
            // map.getView().setCenter(center);
            map.on('postcompose', moveFeature);
            map.render();
        }
    }


    /**
     * @param {boolean}结束动画
     */
    function stopAnimation(ended) {
        animating = false;
        startButton.textContent = '开始运动';
        for (let i = 0; i < routes.length; i++) {
            const item = routes[i];
            //如果动画取消就开始动画
            var coord = ended ? item.routeCoords[item.routeLength - 1] : item.routeCoords[0];
            //将各个线段上的点重新定位到起始位置
            (item.geoMarker.getGeometry()).setCoordinates(coord);
        }

        //移除监听
        map.un('postcompose', moveFeature);
    }

    startButton.addEventListener('click', startAnimation, false);
</script>
</body>
</html>
